/**
 * Area module
 */
app.area = {
	_types : [],
	_stage : null,
	_name : null,
	_description : null,
	_currentArea : null,
	_currentPosition : {x : 0, y : 0},
	_currentLocation : null,
	_currentLocationObject : null,
	_currentActiveLocationObjects : null,

	/**
	 * Area module init
	 */
	init : function() {
		this._stage = $('#locationStage');
		this._name = $('#locationName');
		this._description = $('#locationDescription');
		
		app._currentArea = app.AreaFactory.generateRandomArea('main', 20, 10, [
			{
				coords : '0:0',
				id : 'fountain',
				name : 'Fountain',
				objects : {
					'fountain' : {
						name : 'Fountain',
						handler : function() {
							app.player.setAttr('health', app.player.getAttrMax('health'));
						}
					}
				}
			}, {
				coords : '1:0',
				id : 'sage',
				name : 'Home of sage',
				npc : {
					'sage' : {
						id : 'sage',
						name : 'Sage',
						handler : function() {
							app.dialog.start('sage1');
						}
					}
				}
			}
		]);
		
		this.loadMap();
	},

	/**
	 * Generate area map and add it to area stage
	 */
	loadMap : function() {
		var area = app.AreaFactory.getArea('main');
		var areaStage = $('#areaStage');
		var that = this;
		
		this._currentArea = area;
		
		for (var y = 0; y < area.sizeY; y++) {
			for (var x = 0; x < area.sizeX; x++) {
				var location = area.map[y][x];
				var cell = $('<div>')
					.attr('id', 'location-' + x + '-' + y)
					.addClass('location')
					.addClass(location.id)
					.click(function() {
						var link = $(this);
						var coords = link.attr('id').replace('location-', '').split('-');
						
						if (!link.hasClass('active')) {
							return false;
						}
						
						that.enter(coords[0], coords[1]);
					});
				
				if (x == 0) {
					cell.addClass('first');
				}

				if (x == area.sizeX - 1) {
					cell.addClass('last');
				}
				
				cell.appendTo(areaStage);
			}
		}
		
		this.setActiveZone(0, 0);
	},
	
	/**
	 * Enter the area location
	 *
	 * @param x X coordinate
	 * @param y Y coordinate
	 */
	enter : function(x, y) {
		var location = this._currentArea.map[y][x];
		var enemies = location.enemyTypes && location.enemyTypes.slice(0);
		
		$('#locationBattleActions')[enemies.length > 0 ? 'show' : 'hide']();
		
		this.setActiveZone(x, y);
		
		this._stage.empty();
		this._name.html(location.name);
		this._description.html(location.description);
		
		while (enemies.length > 0) {
			var enemy = enemies.splice(parseInt(Math.random() * enemies.length), 1);
			var enemyType = app.UnitFactory.getType(enemy[0]);
			
			if (enemyType && (enemyType.getAttr('aggression') > (Math.random() * 100))) {
				app.fight.start(enemy, location);
				
				$('#locationBattleActions .sameAgainLink').show();
				
				break;
			}
		}
		
		this.processActions('locationMonsters', location.enemies, 'fight');
		this.processActions('locationNPC', location.npc, 'talk');
		this.processActions('locationObjects', location.objects, 'interact');
		this.processActions('locationCommonActions', location.actions, 'do');
		
		app._currentLocation = location;
		app.show('location');
	},
	
	/**
	 * Set the active zone on the area map
	 *
	 * @param posX X coordinate of zone center
	 * @param posY Y coordinate of zone center
	 */
	setActiveZone : function(posX, posY) {
		this._currentPosition = {x : +posX, y : +posY};
		$(this._currentLocationObject).removeClass('current');
		
		if (this._currentActiveLocationObjects) {
			$.each(this._currentActiveLocationObjects, function(index, item) {
				$(item).removeClass('active');
			});
		}
		this._currentActiveLocationObjects = [];
		
		this._currentLocation = this._currentArea.map[posY][posX];
		this._currentLocationObject = $('#location-' + posX + '-' + posY).addClass('current');
		
		var sight = app.player.getAttr('sight');
		
		for (var x = -sight; x < sight + 1; x++) {
			for (var y = -sight; y < sight + 1; y++) {
				if ((Math.abs(x) + Math.abs(y)) <= sight) {
					var location = $('#location-' + (this._currentPosition.x + x) + '-' + (this._currentPosition.y + y));
					
					if (location.size() > 0) {
						this._currentActiveLocationObjects.push(location.addClass('active'));
					}
				}
			}
		}
	},
	
	/**
	 * Add a object to location stage
	 *
	 * @param obj Object
	 */
	add2stage : function(obj) {
		this._stage.empty();
		obj.appendTo(this._stage);
	},
	
	/**
	 * Process Location actions
	 *
	 * @param group ID of action group
	 * @param obj Object to get actions from
	 * @param linkPrefix Prefix of link hash
	 */
	processActions : function(group, obj, linkPrefix) {
		var group = $('#' + group);
		var list = $('ul', group).empty();
		
		$.each(obj, function(id, action) {
			action.id = action.id || id;
			var handler = action.handler;
			var link = $('<a>').attr({
				title : action.name,
				href : '#' + linkPrefix + '-' + action.id
			}).text(action.name);
			
			if (handler) {
				link.click(function() {
					handler && handler();
					
					return false;
				});
			}
			
			$('<li>').append(link).appendTo(list);
		});
		
		group[list.children().size() > 0 ? 'show' : 'hide']();
	}
};

/**
 * Area factory
 */
app.AreaFactory = {
	// Area types list
	_types : [],
	// Types index (type ID as key, type No as value)
	_typesIndex : [],
	// Areas list
	_areas : [],
	
	/**
	 * Load area types
	 *
	 * @param types Associative array of area types
	 */
	load : function(types) {
		$.each(types, function(id, type) {
			var base = type.base;
			
			if (base) {
				delete type.base;
				
				app.AreaFactory.extendType(id, base, type);
			}
			else {
				app.AreaFactory.createType(id, type);
			}
		});
	},
	
	/**
	 * Create area type
	 *
	 * @param id Area type ID
	 * @param options New area type options
	 */
	createType : function(id, options) {
		options = options || {};
		options.id = id;
		options.enemyTypes = options.enemyTypes || [];
		options.enemies = options.enemies || [];
		options.npc = options.npc || [];
		options.actions = options.actions || [];
		options.objects = options.objects || [];
		
		return this.extendType(id, {}, options);
	},

	/**
	 * Extend given area type with additional options
	 *
	 * @param id New area type ID
	 * @param baseType Area type to use as basis; can be string or object
	 * @param newType New area type options
	 */
	extendType : function(id, baseType, newType) {
		newType.id = id;
		
		if (typeof baseType != 'object') {
			baseType = this.getType(baseType);
		}
		
		return this._typesIndex[id] = this._types.push($.extend(true, {}, baseType, newType)) - 1;
	},
	
	/**
	 * Get area type by ID
	 *
	 * @param type Area type ID
	 * @return Area type
	 */
	getType : function(type) {
		return this._types[this._typesIndex[type]];
	},
	
	/**
	 * Get random area type from cache
	 *
	 * @return Area type
	 */
	getRandomType : function() {
		return this._types[parseInt(Math.random() * this._types.length)];
	},
	
	/**
	 * Create location of given type
	 *
	 * @rapam type Area type
	 * @return Location
	 */
	create : function(type) {
		if (typeof type != 'object') {
			type = this.getType(type);
		}
		
		return $.extend(true, {}, type);
	},
	
	/**
	 * Generate random area map
	 *
	 * @param id Area ID
	 * @param sizeX Horizontal size of area; 20 by default
	 * @param sizeY Vertical size; if not set then sizeX will be used; if sizeX is empty, then will be 20 by default
	 * @param locations Options for specific locations
	 * @return Area map
	 */
	generateRandomArea : function(id, sizeX, sizeY, locations) {
		locations = locations || [];
		sizeX = sizeX || 20;
		sizeY = sizeY || sizeX || 20;
		var area = {
			sizeX : sizeX,
			sizeY : sizeY,
			map : []
		};
		
		for (var y = 0; y < sizeY; y++) {
			area.map[y] = [];
			
			for (var x = 0; x < sizeX; x++) {
				var location = this.create(this.getRandomType());
				
				location.x = x;
				location.y = y;
				area.map[y][x] = location;
			}
		}
		
		$.each(locations, function(index, location) {
			var coords = location.coords.split(':');
			
			delete location.coords;
			
			$.extend(true, area.map[coords[1]][coords[0]], location);
		});
		
		return this._areas[id] = area;
	},
	
	/**
	 * Get area map from cache
	 * 
	 * @param id Area ID
	 * @return Area map
	 */
	getArea : function(id) {
		return this._areas[id];
	}
};

/**
 * Data
 */
(function() {
	var types = {
		'base' : {
			name : 'Base',
			actions : {
				'goback' : {
					name : 'Back to area',
					handler : function() {
						app.show('area');
					}
				}
			}
		},
		
		'field' : {
			name : 'Field',
			base : 'base'
		},
		
		'forest' : {
			name : 'Forest',
			base : 'base',
			enemyTypes : ['rat']
		},
		
		'dense-forest' : {
			name : 'Dense forest',
			base : 'forest',
			enemyTypes : ['rat', 'big-rat']
		}
	};
	
	app.AreaFactory.load(types);
})();